package com.example;

import java.io.ByteArrayOutputStream;
import java.util.Arrays;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;

public class OpusCodec {
    static {
        System.loadLibrary("opus_jni");
    }

    private long ctxPtr;

    // 初始化编码器
    public native long initEncoder(int sampleRate, int channels, int bitrate);

    // 初始化解码器
    public native long initDecoder(int sampleRate, int channels);

    // 编码一帧
    public native byte[] encodeFrame(long ctxPtr, byte[] pcmData);

    // 解码一帧
    public native byte[] decodeFrame(long ctxPtr, byte[] opusData);

    // 释放资源
    public native void destroy(long ctxPtr);

    private static void processData(OpusCodec codec, long encCtx, long decCtx, byte[] data) {
        byte[] encoded = codec.encodeFrame(encCtx, data);
        for (byte b: encoded) {
            System.out.printf("%02X ", b);
        }
        System.out.println();

        // // 解码
        // byte[] pcmOut = codec.decodeFrame(decCtx, encoded);

        // System.out.println("Decoded samples: " + pcmOut.length);
        // for (byte b: pcmOut) {
        // System.out.printf("%02X ", b);
        // }
        // System.out.println();
    }

    public static void main(String[] args) {
        final int READ_BUFFER_SIZE = 1024;
        final int PROCESS_BUFFER_SIZE = 640; // 20ms

        OpusCodec codec = new OpusCodec();

        // 初始化编码器 (VOIP模式)
        long encCtx = codec.initEncoder(16000, 1, 16000);

        // 初始化解码器
        long decCtx = codec.initDecoder(16000, 1);

        String filePath = "out.pcm";
        try (FileInputStream fis = new FileInputStream(filePath)) {
            Queue<Byte> fifo = new LinkedList<>();
            byte[] buffer = new byte[READ_BUFFER_SIZE];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                // 将读取的数据放入 FIFO
                for (int i = 0; i < bytesRead; i++) {
                    fifo.add(buffer[i]);
                }
                // 当 FIFO 中的数据达到 PROCESS_BUFFER_SIZE 时，进行处理
                while (fifo.size() >= PROCESS_BUFFER_SIZE) {
                    byte[] processBuffer = new byte[PROCESS_BUFFER_SIZE];
                    for (int i = 0; i < PROCESS_BUFFER_SIZE; i++) {
                        processBuffer[i] = fifo.poll();
                    }
                    processData(codec, encCtx, decCtx, processBuffer);
                }
            }
            // 处理 FIFO 中剩余的数据，不足的部分填充 0
            if (!fifo.isEmpty()) {
                byte[] processBuffer = new byte[PROCESS_BUFFER_SIZE];
                int index = 0;
                while (!fifo.isEmpty() && index < PROCESS_BUFFER_SIZE) {
                    processBuffer[index++] = fifo.poll();
                }
                // 填充 0
                while (index < PROCESS_BUFFER_SIZE) {
                    processBuffer[index++] = 0;
                }
                processData(codec, encCtx, decCtx, processBuffer);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 清理资源
        codec.destroy(encCtx);
        codec.destroy(decCtx);
    }
}